home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Technotools
/
Technotools (Chestnut CD-ROM)(1993).ISO
/
misc_pto
/
basic-c
/
basic.c
next >
Wrap
C/C++ Source or Header
|
1988-12-21
|
16KB
|
874 lines
/*********************************************************
* A tiny BASIC interpreter adapted from: *
* *
* C: Power User's Guide *
* by *
* Herbert Schildt *
* *
* Published by Osborne/McGraw-Hill, *
* Copyright 1987, Osborne/McGraw-Hill. *
*********************************************************/
#include <stdio.h>
#include <setjmp.h>
#include <ctype.h>
#include <fcntl.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifdef MSDOS
#include <dos.h>
#include <stdlib.h>
#else
#include "term.h"
#endif
#include "defines.h"
jmp_buf e_buf; /* hold environment for longjmp() */
struct commands { /* keyword lookup table */
char command[20]; /* command name */
char tokenid; /* command token value */
} table[] = { /* entries here must be lowercase */
"print", PRINT,
"input", INPUT,
"if", IF,
"then", THEN,
"goto", GOTO,
"for", FOR,
"next", NEXT,
"to", TO,
"gosub", GOSUB,
"return", RETURN,
"shell", SHELL,
"move", MOVE,
"cls", CLS,
"rem", REMARK,
"beep", BEEP,
"sleep", SLEEP,
"time", TIME,
"date", DATE,
"end", END,
"", END /* mark end of table */
};
struct for_stack {
int var; /* counter variable */
int target; /* target value */
char *loc;
} fstack[FOR_NEST]; /* stack for FOR/NEXT loop */
struct for_stack fpop();
char token[BUFSIZ];
char *p_buf;
char *gpop();
char *load();
char *prog; /* holds expression to be analyzed */
char *gstack[SUB_NEST]; /* stack for gosub */
int tokentype;
int tokenstat;
int ftos; /* index to top of FOR stack */
int gtos; /* index to top of gosub stack */
int variables[26] = { /* 26 user variables, A-Z */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
void b_print(), geteol(), b_goto(), b_sleep();
void b_if (), b_for(), b_next(), fpush(), b_input();
void getexp(), level2(), level3(), level4(), level5();
void level6(), primitive(), arith(), unary(), b_shell();
void b_error(), putback(), b_gosub(), b_return(), b_remark();
void gpush(), b_move(), b_cls(), b_beep(), b_time(), b_date();
main(argc, argv)
int argc;
char *argv[];
{
if (argc != 2) {
fprintf (stderr, "usage: basic <progname>[.bas]\n");
exit(1);
}
setbuf(stdin, NULL);
setbuf(stdout, NULL);
setbuf(stderr, NULL);
#ifndef MSDOS
gettermtype();
#endif
/* load the program to execute */
if ((p_buf = load(argv[1])) == NULL) {
fprintf(stderr, "\nBASIC: can not load %s", argv[1]);
exit(1);
}
/* initialize the long jump buffer */
if (setjmp(e_buf))
exit(1);
prog = p_buf;
initlabels(token); /* find the labels in the program */
ftos = 0; /* initialize the FOR stack index */
gtos = 0; /* initialize the gosub stack index */
do {
/* get first token to get ball rolling */
tokentype = gettoken(token);
/* check for assignment statement */
if (tokentype == VARIABLE) {
putback(token); /* put token back */
assignment(token); /* must be assignment */
} else { /* token must be a command */
switch (tokenstat) {
case PRINT:
b_print(token);
break;
case GOTO:
b_goto(token);
break;
case IF:
b_if(token);
break;
case FOR:
b_for(token);
break;
case NEXT:
b_next();
break;
case INPUT:
b_input(token);
break;
case GOSUB:
b_gosub(token);
break;
case RETURN:
b_return();
break;
case SHELL:
b_shell(token);
break;
case CLS:
b_cls();
break;
case MOVE:
b_move(token);
break;
case REMARK:
b_remark();
break;
case BEEP:
b_beep(token);
break;
case SLEEP:
b_sleep(token);
break;
case TIME:
b_time();
break;
case DATE:
b_date();
break;
case END:
b_cls();
tokenstat = FINISHED;
break;
}
} /* end of 'else' */
} while (tokenstat != FINISHED);
exit(0);
}
/*
* load - allocate sufficient memory if possible,
* then load the user BASIC program into memory.
* returns pointer to program in allocated memory.
*/
char *
load(fname)
char *fname;
{
int fp, r;
char ifile[15], *b;
struct stat st;
strcpy(ifile, fname); /* save partial name */
if (!strchr(ifile, '.')) /* check for extension */
strcat(ifile, ".bas"); /* add ".bas" to file name */
/* attempt to open file */
if ((fp = open (ifile, O_RDONLY | O_TEXT)) == EOF) {
fprintf (stderr, "\nBASIC: program open error: %s\n", ifile);
return (NULL);
}
/* determine amount of memory required */
if (fstat (fp, &st)) {
fprintf (stderr, "\nBASIC: program status error\n");
close (fp);
return (NULL);
} else
fprintf(stderr, "\nBASIC: program size: %d\n", st.st_size);
/* allocate memory for the program */
if ((b = (char *)calloc(1, st.st_size + 1)) == NULL) {
fprintf (stderr, "\nBASIC: memory allocation failure\n");
close (fp);
return (NULL);
}
/* read file into zeroed memory */
r = read (fp, b, (unsigned )st.st_size);
/* check for EOF instead of amount read */
/* in consideration of the MSDOS O_TEXT */
/* mode, which could be less due to CRLF */
/* translation to LF only */
if (r == EOF) {
fprintf(stderr, "\nBASIC: program file load error\n");
close (fp);
return (NULL);
} else
fprintf(stderr, "BASIC: loaded size: %d\n", r);
close (fp); /* done with file, so close it */
return (b); /* return pointer to memory */
}
/*
* gettoken - get a token and place it in gtoken.
*/
int
gettoken(gtoken)
char *gtoken;
{
char *temp;
tokentype = 0;
tokenstat = 0;
temp = gtoken;
if (*prog == NULL) { /* end of file */
gtoken[0] = NULL;
tokenstat = FINISHED;
return(DELIMITER);
}
while (iswhite(*prog))
++prog; /* skip over white space */
if (*prog == '\n') { /* LF */
++prog;
if (*prog)
tokenstat = EOL;
else
tokenstat = FINISHED;
gtoken[0] = '\n';
gtoken[1] = NULL;
return (DELIMITER);
}
if (strchr("+-*^/%=;(),><", *prog)) { /* delimiter */
*temp = *prog; /* white space already skipped */
prog++; /* advance to next position */
temp++;
*temp = NULL;
return (DELIMITER);
}
if (*prog == '"') { /* quoted string */
prog++; /* step over quote */
while (*prog && *prog != '"' && *prog != '\n')
*temp++ = *prog++; /* copy string to temp */
if (*prog == '\n' || *prog == NULL)
b_error(16); /* unbalanced quotes */
prog++; /* step over end quote */
*temp = NULL; /* terminate temp */
return(QUOTE);
}
if (isdigit(*prog)) { /* number */
while (!isdelim(*prog))
*temp++ = *prog++;
*temp = NULL;
return(NUMBER);
}
if (isalpha(*prog)) { /* var or command */
while (!isdelim(*prog))
*temp++ = *prog++;
tokentype = STRING;
}
*temp = NULL;
/* see if a string is a command or a variable */
if (tokentype == STRING) {
tokenstat = look_up(gtoken); /* convert to internal rep */
if (!tokenstat)
tokentype = VARIABLE;
else
tokentype = COMMAND; /* is a command */
}
return (tokentype);
}
/*
* assignment - assign a value to a variable
*/
assignment(atoken)
char *atoken;
{
int var, value;
/* get the variable name */
tokentype = gettoken(atoken);
if (!isalpha (*atoken)) {
b_error(4);
return;
}
var = toupper(*atoken) - 'A'; /* calculate the variable's offset */
/* get the equals sign */
tokentype = gettoken(atoken);
if (*atoken != '=') {
b_error(3);
return;
}
/* get the value to assign to variable */
getexp(atoken, &value);
/* assign the value */
variables[var] = value;
return;
}
/*
* geteol - find the start of the next line
*/
void
geteol()
{
while (*prog != '\n' && *prog != NULL)
++prog;
if (*prog)
prog++;
return;
}
/*
* b_if - execute an IF statement
*/
void
b_if(itoken)
char *itoken;
{
int x , y, cond;
char op;
getexp(itoken, &x); /* get left expression */
tokentype = gettoken(itoken); /* get the operator */
if (!strchr("=<>", *itoken)) {
b_error(0); /* not a legal operator */
return;
}
op = *itoken;
getexp(itoken, &y); /* get right expression */
cond = 0; /* determine the outcome */
switch (op) {
case '<':
if (x < y)
cond = 1;
break;
case '>':
if (x > y)
cond = 1;
break;
case '=':
if (x == y)
cond = 1;
break;
}
if (cond) { /* true, so process target of IF */
tokentype = gettoken(itoken);
if (tokenstat != THEN) {
b_error(8);
return;
}
} else /* execution starts on next line */
geteol(); /* find start of next line */
return;
}
/*
* b_for - execute a FOR loop
*/
void
b_for(ftoken)
char *ftoken;
{
struct for_stack i;
int value;
tokentype = gettoken(ftoken); /* read the control variable */
if (!isalpha(*ftoken)) {
b_error(4);
return;
}
i.var = toupper(*ftoken) - 'A'; /* save its index */
tokentype = gettoken(ftoken); /* read the equals sign */
if (*ftoken != '=') {
b_error(3);
return;
}
getexp(ftoken, &value); /* get initial value */
variables[i.var] = value;
tokentype = gettoken(ftoken);
if (tokenstat != TO)
b_error(9); /* read and discard the TO */
getexp(ftoken, &i.target); /* get target value */
/* if loop can execute at least once, push info on stack */
if (value >= variables[i.var]) {
i.loc = prog;
fpush(i);
} else /* otherwise, skip loop code */
while (tokenstat != NEXT)
tokentype = gettoken(ftoken);
return;
}
/*
* next - execute a NEXT statement
*/
void
b_next()
{
struct for_stack i;
i = fpop(); /* read the loop info */
variables[i.var]++; /* increment control variable */
if (variables[i.var] > i.target)
return; /* all done */
fpush(i); /* otherwise, restore the info */
prog = i.loc; /* loop */
return;
}
/*
* fpush - push function for the FOR stack
*/
void
fpush(i)
struct for_stack i;
{
if (ftos > FOR_NEST)
b_error(10);
fstack[ftos] = i;
ftos++;
return;
}
/*
* fpop - pop function from the FOR stack
*/
struct for_stack fpop()
{
ftos--;
if (ftos < 0)
b_error(11);
return(fstack[ftos]);
}
/*
* gpush - gosub stack push function
*/
void
gpush(s)
char *s;
{
gtos++;
if (gtos == SUB_NEST) {
b_error(12);
return;
}
gstack[gtos] = s;
return;
}
/*
* gpop - gosub stack pop function
*/
char *
gpop()
{
if (gtos == 0) {
b_error(13);
return 0;
}
return(gstack[gtos--]);
}
/*
* getexp - entry point into parser
*/
void
getexp(xtoken, result)
char *xtoken;
int *result;
{
tokentype = gettoken(xtoken);
if (!*xtoken) {
b_error(2);
return;
}
level2(xtoken, result);
putback(xtoken); /* return last token to input */
return;
}
/*
* level2 - Add or subtract two terms
*/
void
level2(xtoken, result)
char *xtoken;
int *result;
{
register char op;
int hold;
level3(xtoken, result);
while ((op = *xtoken) == '+' || op == '-') {
tokentype = gettoken(xtoken);
level3(xtoken, &hold);
arith(op, result, &hold);
}
return;
}
/*
* level3 - multiply or divide two factors.
*/
void
level3(xtoken, result)
char *xtoken;
int *result;
{
register char op;
int hold;
level4(xtoken, result);
while ((op = *xtoken) == '*' || op == '/' || op == '%') {
tokentype = gettoken(xtoken);
level4(xtoken, &hold);
arith(op, result, &hold);
}
return;
}
/*
* level4 - process integer exponent.
*/
void
level4(xtoken, result)
char *xtoken;
int *result;
{
int hold;
level5(xtoken, result);
if (*xtoken == '^') {
tokentype = gettoken(xtoken);
level4(xtoken, &hold);
arith('^', result, &hold);
}
return;
}
/*
* level5 - process a unary + or -.
*/
void
level5(xtoken, result)
char *xtoken;
int *result;
{
register char op;
op = NULL;
if ((tokentype == DELIMITER) && *xtoken == '+' || *xtoken == '-') {
op = *xtoken;
tokentype = gettoken(xtoken);
}
level6(xtoken, result);
if (op)
unary(op, result);
return;
}
/*
* level6 - process parenthesized expression.
*/
void
level6(xtoken, result)
char *xtoken;
int *result;
{
if ((*xtoken == '(') && (tokentype == DELIMITER)) {
tokentype = gettoken(xtoken);
level2(xtoken, result);
if (*xtoken != ')')
b_error(1);
tokentype = gettoken(xtoken);
} else
primitive(xtoken, result);
return;
}
/*
* primitive - find value of number or variable.
*/
void
primitive(xtoken, result)
char *xtoken;
int *result;
{
switch (tokentype) {
case VARIABLE:
*result = getval(xtoken);
tokentype = gettoken(xtoken);
return;
case NUMBER:
*result = atoi(xtoken);
tokentype = gettoken(xtoken);
return;
default:
b_error(0);
}
return;
}
/*
* arith - perform the specified arithmetic.
*/
void
arith(o, r, h)
char o;
int *r, *h;
{
register int t, ex;
switch (o) {
case '-':
*r = *r - *h;
break;
case '+':
*r = *r + *h;
break;
case '*':
*r = *r * *h;
break;
case '/':
*r = (*r) / (*h);
break;
case '%':
t = (*r) / (*h);
*r = *r - (t * (*h));
break;
case '^':
ex = *r;
if (*h == 0) {
*r = 1;
break;
}
for (t = *h - 1; t > 0; --t)
*r = (*r) * ex;
break;
}
return;
}
/*
* unary - reverse the sign.
*/
void
unary(o, r)
char o;
int *r;
{
if (o == '-')
*r = -(*r);
return;
}
/*
* getval - find the value of a variable.
*/
int
getval(s)
char *s;
{
if (!isalpha(*s)) {
b_error(4); /* not a variable */
return 0; /* NOTREACHED */
}
return variables[toupper(*s)-'A'];
}
/*
* putback - return a token to input stream.
*/
void
putback(ptoken)
char *ptoken;
{
char *t;
t = ptoken;
for (; *t; t++)
prog--;
return;
}
/*
* b_error - display an error message.
*/
void
b_error(error)
int error;
{
static char *e[] = {
"syntax error", /* 0 */
"unbalanced parentheses", /* 1 */
"no expression present", /* 2 */
"equals sign expected", /* 3 */
"not a variable", /* 4 */
"label table full", /* 5 */
"duplicate label", /* 6 */
"undefined label", /* 7 */
"THEN expected", /* 8 */
"TO expected", /* 9 */
"too many nested FOR loops", /* 10 */
"NEXT without FOR", /* 11 */
"too many nested GOSUBs", /* 12 */
"RETURN without GOSUB", /* 13 */
"invalid row number", /* 14 */
"invalid column number", /* 15 */
"unbalanced double quotes" /* 16 */
};
fprintf (stderr, "\nBASIC: %s\n", e[error]);
longjmp(e_buf, 1); /* return to save point */
}
/*
* loop_up - find a token's internal representation in the token table.
*/
int
look_up(s)
char *s;
{
register int i;
char *p;
/* convert to lowercase */
p = s;
while (*p) {
*p = tolower(*p);
p++;
}
/* see if token is in table */
for (i = 0; *table[i].command; i++)
if (strcmp(table[i].command, s) == 0)
return (table[i].tokenid); /* valid command */
return(0); /* unknown command */
}
/*
* isdelim - return true if c is a delimiter.
*/
isdelim(c)
char c;
{
if (strchr(" ;,+-<>/*%^=()", c) || c == 9 || c == '\n' || c == 0)
return 1;
return 0;
}
/*
* iswhite - return 1 if c is space or tab.
*/
iswhite(c)
char c;
{
if (c == ' ' || c == '\t')
return 1;
else
return 0;
}